package jp.seraph.jsade.math;

import java.text.DecimalFormat;

import org.apache.commons.math.geometry.Vector3D;

/**
 * 3次元上の座標を表すクラス Apache Commons Mathに依存する
 *
 * このクラスのインスタンスはimmutableであり、その内部状態が変更されることはない。
 */
public class Point3D {
    public Point3D(double x, double y, double z) {
        this(x, y, z, 0.1);
    }

    /**
     *
     * @param x
     * @param y
     * @param z
     * @param aTolerance ある点とある点が同一であると認識する許容誤差
     */
    public Point3D(double x, double y, double z, double aTolerance) {
        mX = x;
        mY = y;
        mZ = z;
        mTolerance = aTolerance;
    }

    /**
     * (x, y, z) = (0, 0, 0)であるPoint3Dを作成する。
     */
    public Point3D() {
        this(0, 0, 0);
    }

    /**
     * 原点に引数のベクトルをたした座標を作成する。
     *
     * @param aBaseVector
     */
    public Point3D(Vector3D aBaseVector) {
        this(aBaseVector.getX(), aBaseVector.getY(), aBaseVector.getZ());
    }

    /**
     * 原点(x,y,z) = (0,0,0)を表すPoint3D
     */
    public static final Point3D origin = new Point3D();

    /**
     * aOriginを原点とした場合の、aTargetの座標を表すPoint3Dのインスタンスを作成する。
     *
     * @param aOrigin
     * @param aTarget
     */
    public Point3D(Vector3D aOrigin, Vector3D aTarget) {
        this(aTarget.subtract(aOrigin));
    }

    private double mX, mY, mZ;
    private double mTolerance;

    /**
     * x座標を返す
     * @return x座標
     */
    public double getX() {
        return mX;
    }

    /**
     * y座標を返す
     * @return y座標
     */
    public double getY() {
        return mY;
    }

    /**
     * z座標を返す
     * @return z座標
     */
    public double getZ() {
        return mZ;
    }

    /**
     * この座標に引数のベクトルを足した座標を返す
     *
     * @param aV
     * @return 演算後の座標
     */
    public Point3D add(Vector3D aV) {
        return new Point3D(mX + aV.getX(), mY + aV.getY(), mZ + aV.getZ());
    }

    /**
     * この座標から引数のベクトルを引いた座標を返す
     *
     * @param aV
     * @return 演算後の座標
     */
    public Point3D subtract(Vector3D aV) {
        return new Point3D(mX - aV.getX(), mY - aV.getY(), mZ - aV.getZ());
    }

    /**
     * aPからthisへのベクトルを取得する
     *
     * @param aP
     * @return
     */
    public Vector3D subtract(Point3D aP) {
        return new Vector3D(mX - aP.getX(), mY - aP.getY(), mZ - aP.getZ());
    }

    /**
     * 原点(0,0,0)からthisへのベクトルを取得する
     *
     * @return
     */
    public Vector3D getVector() {
        return new Vector3D(mX, mY, mZ);
    }

    /**
     * 自身と対象座標との距離を返す
     *
     * @param aTarget 対象座標
     * @return 距離
     */
    public double getDistance(Point3D aTarget){
        return this.subtract(aTarget).getNorm();
    }

    /**
     * オブジェクトの文字列表現を作成して返す。
     */
    
    public String toString() {
        return "(" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")";
    }

    public String toString(int aFractionDigits){
        return "(" + toString(this.getX(), aFractionDigits) + ", " + toString(this.getY(), aFractionDigits) + ", " + toString(this.getZ(), aFractionDigits) + ")";
    }

    private String toString(double aValue, int aFractionDigits){
        DecimalFormat tFormat = new DecimalFormat();
        tFormat.applyPattern("0");
        tFormat.setMaximumFractionDigits(aFractionDigits);
        tFormat.setMinimumFractionDigits(aFractionDigits);

        Double tObjValue=new Double(aValue);
        return tFormat.format(tObjValue);
    }

    /**
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    
    public boolean equals(Object aObj) {
        if (!(aObj instanceof Point3D)) {
            return false;
        }

        Point3D tObj = (Point3D) aObj;

        return this.getDistance(tObj) <= mTolerance;
    }

    /**
     *
     * @see java.lang.Object#hashCode()
     */
    
    public int hashCode() {
        return super.hashCode();
    }

}
